#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

using std::unordered_map;
using std::vector;
using std::string;
using std::cout;
using std::cerr;
using std::endl;

class Sock
{
public:
    static int Socket()
    {
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
        {
            std::cerr << "socket error: " << strerror(errno) << std::endl;
            return -1;
        }
        int opt = 1;
        // server异常退出导致一直处于TIME_WAIT状态，设置该API可以重复绑定同一个端口号进行快速重启server(作用是屏蔽OS检测TIME_WAIT状态算法)
        setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, 
                        ( const char* )&opt, sizeof(opt));
        return sockfd;
    }

    static void Bind(int sockfd, std::string& ip, uint16_t port)
    {
        sockaddr_in serverData;
        socklen_t len = sizeof(serverData);
        memset(&serverData, 0, len);

        // 填充网络通讯基本信息
        serverData.sin_family = AF_INET;
        serverData.sin_port = htons(port);
        serverData.sin_addr.s_addr = ip.empty() ? inet_addr(INADDR_ANY) : inet_addr(ip.c_str());
        if (bind(sockfd, (const sockaddr*)&serverData, len) < 0)
        {
            std::cerr << "bind error: " << strerror(errno) << std::endl;
            exit(1);
        }
    }

    static void Listen(int sockfd, int backlog)
    {
        // 全连接等待队列设置为n + 1个，一共可以等待获取n + 1个连接
        if (listen(sockfd, backlog) < 0)
        {
            std::cerr << "listen error: " << strerror(errno) << std::endl;
            exit(2);
        }
    }

    static int Accept(int listenSockfd, string* ClientIp, uint16_t* ClientPort)
    {
        sockaddr_in ClientData;
        socklen_t len = sizeof(ClientData);
        memset(&ClientData, 0, len);

        int serverSockfd = accept(listenSockfd, (sockaddr*)&ClientData, &len);
        if (serverSockfd < 0)
        {
            std::cerr << "accpet error: " << strerror(errno) << std::endl;
            return -1;
        }

        *ClientIp = inet_ntoa(ClientData.sin_addr);
        *ClientPort = ntohs(ClientData.sin_port);

        return serverSockfd;
    }
};